package com.gec.anan.map.service.impl;

import com.gec.anan.common.constant.RedisConstant;
import com.gec.anan.common.constant.SystemConstant;
import com.gec.anan.common.util.LocationUtil;
import com.gec.anan.driver.client.DriverInfoFeignClient;
import com.gec.anan.map.repository.OrderServiceLocationRepository;
import com.gec.anan.map.service.LocationService;
import com.gec.anan.model.entity.driver.DriverSet;
import com.gec.anan.model.entity.map.OrderServiceLocation;
import com.gec.anan.model.form.map.OrderServiceLocationForm;
import com.gec.anan.model.form.map.SearchNearByDriverForm;
import com.gec.anan.model.form.map.UpdateDriverLocationForm;
import com.gec.anan.model.form.map.UpdateOrderLocationForm;
import com.gec.anan.model.vo.map.NearByDriverVo;
import com.gec.anan.model.vo.map.OrderLocationVo;
import com.gec.anan.model.vo.map.OrderServiceLastLocationVo;
import com.gec.anan.order.client.OrderInfoFeignClient;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class LocationServiceImpl implements LocationService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private DriverInfoFeignClient driverInfoFeignClient;

    @Autowired
    private OrderServiceLocationRepository orderServiceLocationRepository;

    @Autowired
    private OrderInfoFeignClient orderInfoFeignClient;
    /**
     * 司机更新位置信息
     *
     * @param updateDriverLocationForm
     * @return
     */
    @Override
    public Boolean updateDriverLocation(UpdateDriverLocationForm updateDriverLocationForm) {
        //获取point对象
        Point point = new Point(updateDriverLocationForm.getLongitude().doubleValue(), updateDriverLocationForm.getLatitude().doubleValue());
        redisTemplate.opsForGeo().add(RedisConstant.DRIVER_GEO_LOCATION
                , point, updateDriverLocationForm.getDriverId().toString());
        return true;
    }

    /**
     * 司机关闭接单，删除位置信息
     *
     * @param driverId
     * @return
     */
    @Override
    public Boolean removeDriverLocation(Long driverId) {
        redisTemplate.opsForGeo().remove(RedisConstant.DRIVER_GEO_LOCATION, driverId.toString());
        return true;
    }

    /**
     * 获取附近司机信息
     *
     * @param searchNearByDriverForm
     * @return
     */
    @Override
    public List<NearByDriverVo> searchNearByDriver(SearchNearByDriverForm searchNearByDriverForm) {
        //定义经纬度
        Point point = new Point(searchNearByDriverForm.getLongitude().doubleValue(), searchNearByDriverForm.getLatitude().doubleValue());
        //定义距离
        Distance distance = new Distance(SystemConstant.NEARBY_DRIVER_RADIUS, RedisGeoCommands.DistanceUnit.KILOMETERS);
        //获取中心点
        Circle circle = new Circle(point, distance);
        //获取geo参数
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                .includeDistance() //返回包含距离
                .includeCoordinates() //包含坐标
                .sortAscending(); //升序
        //调用redis查询
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius(RedisConstant.DRIVER_GEO_LOCATION, circle, args);
        //获取返回结果的集合
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = results.getContent();
        log.info("查询附近司机信息：{}", content);
        //常见返回集合
        ArrayList<NearByDriverVo> nearByDriverVos = new ArrayList<>();
        //判断是否为空
        if (!CollectionUtils.isEmpty(content)) {
            //获取迭代器
            Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = content.iterator();
            //循环遍历
            while (iterator.hasNext()) {
                //获取这个元素
                GeoResult<RedisGeoCommands.GeoLocation<String>> item = iterator.next();
                //获取司机id
                Long driverId = Long.parseLong(item.getContent().getName());
                //获取司机距离乘客的位置距离
                BigDecimal newDistance = new BigDecimal(item.getDistance().getValue()).setScale(2, RoundingMode.HALF_UP);
                log.info("司机：{}，距离：{}", driverId, item.getDistance().getValue());
                //查找司机个性化信息
                DriverSet driverSet = driverInfoFeignClient.getDriverSet(driverId).getData();
                //判断司机可接受上车的距离
                if (driverSet.getAcceptDistance().doubleValue() != 0 && driverSet.getAcceptDistance().subtract(newDistance).doubleValue() < 0) {
                    continue;
                }
                //司机可以接受路程
                if (driverSet.getOrderDistance().doubleValue() != 0 && driverSet.getOrderDistance().subtract(searchNearByDriverForm.getMileageDistance()).doubleValue() < 0) {
                    continue;
                }
                //符合附近可接单的司机
                NearByDriverVo nearByDriverVo = new NearByDriverVo();
                nearByDriverVo.setDriverId(driverId);
                nearByDriverVo.setDistance(newDistance);
                nearByDriverVos.add(nearByDriverVo);
            }
        }
        log.info("附近司机信息：{}", nearByDriverVos);
        return nearByDriverVos;
    }

    //更新司机位置到缓存
    @Override
    public Boolean updateOrderLocationToCache(UpdateOrderLocationForm updateOrderLocationForm) {
        OrderLocationVo orderLocationVo = new OrderLocationVo();
        orderLocationVo.setLatitude(updateOrderLocationForm.getLatitude());
        orderLocationVo.setLongitude(updateOrderLocationForm.getLongitude());
        String key = RedisConstant.UPDATE_ORDER_LOCATION + updateOrderLocationForm.getOrderId();
        redisTemplate.opsForValue().set(key, orderLocationVo);
        return true;
    }

    //乘客端获取司机位置信息
    @Override
    public OrderLocationVo getCacheOrderLocation(Long orderId) {
        String key = RedisConstant.UPDATE_ORDER_LOCATION + orderId;
        OrderLocationVo orderLocationVo = (OrderLocationVo) redisTemplate.opsForValue().get(key);
        return orderLocationVo;
    }

    //司机保存位置服务
    @Override
    public Boolean saveOrderServiceLocation(List<OrderServiceLocationForm> orderLocationServiceFormList) {
        //使用集合
        ArrayList<OrderServiceLocation> list = new ArrayList<>();
        orderLocationServiceFormList.forEach(item -> {
            OrderServiceLocation location = new OrderServiceLocation();
            BeanUtils.copyProperties(item, location);
            location.setId(ObjectId.get().toString());
            location.setCreateTime(new Date());
            list.add(location);
        });
        orderServiceLocationRepository.saveAll(list);
        return true;
    }

    //乘客获取最后一个位置
    @Override
    public OrderServiceLastLocationVo getOrderServiceLastLocation(Long orderId) {
        //封装参数
        OrderServiceLocation location = new OrderServiceLocation();
        location.setOrderId(orderId);
        Example<OrderServiceLocation> example = Example.of(location);
        Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
        List<OrderServiceLocation> serviceLocations = orderServiceLocationRepository.findAll(example, sort);
        OrderServiceLocation orderServiceLocation = serviceLocations.get(0);
        OrderServiceLastLocationVo orderServiceLastLocationVo = new OrderServiceLastLocationVo();
        BeanUtils.copyProperties(orderServiceLocation, orderServiceLastLocationVo);
        return orderServiceLastLocationVo;
    }

    //计算实际历程距离
    @Override
    public BigDecimal calculateOrderRealDistance(Long orderId) {
        //从mongodb获取位置信息排序
        List<OrderServiceLocation> list =
                orderServiceLocationRepository.findByOrderIdOrderByCreateTimeAsc(orderId);
        //遍历
        //实际距离
        double realDistance = 0;
        if(!CollectionUtils.isEmpty(list)){
            for (int i = 0,size = list.size()-1; i <size; i++) {
              //获取相邻位置参数
                OrderServiceLocation location1 = list.get(i);
                OrderServiceLocation location2 = list.get(i+1);
                //调用工具类计算距离
                double distance = LocationUtil.getDistance(location1.getLatitude().doubleValue(), location1.getLongitude().doubleValue(),
                        location2.getLatitude().doubleValue(), location2.getLongitude().doubleValue());
                realDistance += distance;
            }
        }
        //TODO 模拟测试
        //测试过程中，没有真正代驾，实际代驾GPS位置没有变化，模拟：实际代驾里程 = 预期里程 + 5
        if(realDistance == 0) {
            return orderInfoFeignClient.getOrderInfo(orderId).getData().getExpectDistance().add(new BigDecimal("5"));
        }

        return new BigDecimal(realDistance);
    }
}
